﻿#region

using System;
using System.Net;
using System.Net.Sockets;
using System.Threading;
using Jiubang.Collections;
using Jiubang.Communication.Common;
using Jiubang.Communication.Server;

#endregion

namespace Jiubang.Communication.Clients{
    /// <summary>
    ///   Tcp包发送任务线程
    ///   默认使用添加方式添加消息头信息
    /// </summary>
    public sealed class TcpSender : TcpWorker, IMessageSender{
        private readonly TimerCallback closeCallBack;
        private readonly ProducerConsumerQueue<MessageContext> queue;
        private readonly IPEndPoint remoteEp;
        private bool reSet;

        /// <param name = "tcpClient">封装的对象</param>
        /// <param name = "remoteEp">远程终端</param>
        /// <param name = "queue">缓冲队列</param>
        public TcpSender(TcpClient tcpClient, IPEndPoint remoteEp, ProducerConsumerQueue<MessageContext> queue)
            : base(tcpClient, remoteEp){
            this.queue = queue;
            this.remoteEp = remoteEp;
            Name = "TcpSender" + remoteEp;
            closeCallBack = CloseWhileOvertime;
        }

        #region IMessageSender Members

        /// <summary>
        ///   将指定包压入发送队列
        /// </summary>
        public bool Send(MessageContext data){
            if (data.Segment.array == null) throw new ArgumentNullException("data");
            return queue.Enqueue(data);
        }

        #endregion

        /// <summary>
        ///   在重连完成之后使用该方法优先发送之前累积的消息
        /// </summary>
        /// <param name = "message">优先发送的消息</param>
        public void SendAfterReconnected(MessageContext message){
            if (WorkerThread != Thread.CurrentThread) throw new Exception("方法由非当前对象工作线程调用");

            SendInternal(message, client.Client);
        }

        /// <summary>
        ///   建立连接
        /// </summary>
        public bool TryConnect(TcpClient tcpClient){
            lock (tcpClient){
                var socket = tcpClient.Client;
                if (socket != null && socket.Connected) return true;
                if (socket != null) socket.Close();
                socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
                var timer = new Timer(closeCallBack, socket, -1, -1);
                try{
                    timer.Change(1000, -1);
                    socket.Connect(remoteEp);
                } finally{
                    timer.Dispose();
                }
                tcpClient.Client = socket;
                reSet = socket.Connected;
                if (reSet && logger.IsInfoEnabled)
                    logger.Info("已重建从[" + socket.LocalEndPoint + "]到[" + socket.RemoteEndPoint + "]的链接。");
                return reSet;
            }
        }

        private static void CloseWhileOvertime(object state){
            var socket = (Socket) state;
            if (!socket.Connected) socket.Close();
        }

        /// <summary>
        ///   从队列中读取数据，添加长度信息后发送
        /// </summary>
        /// <returns></returns>
        protected override bool WaitAfterExecution(Socket socket){
            var data = queue.Dequeue();
            //重启服务后，链接重置，需要重新获取socket对象。
            if (reSet){
                socket = client.Client;
                reSet = false;
            }
            SendInternal(data, socket);
            if (BufferManager.Instance.IsEnable) BufferManager.Instance.FreeBuffer(data);
            return false;
        }

        private void SendInternal(MessageContext data, Socket socket){
            var len = (UInt16) data.Segment.offset;
            var hander = BitConverter.GetBytes((UInt16) (len - 2));
            hander.CopyTo(data.Segment.array, 0);
            //socket.Send(hander, SocketFlags.None);
            var result = socket.Send(data.Segment.array, 0, len, SocketFlags.None);
            if (result != len) logger.Warn(string.Format("待发送长度：{0},实际发送长度:{1}", len, result));
        }
    }
}